package mlogger

import (
	"fmt"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"strings"
)

var defaultTraceLogger *TraceLogger

type TraceKeyValue struct {
	Key      string
	Value    string
	FmtValue string
}
type TraceLogger struct {
	logger *zap.SugaredLogger
	stack  []TraceKeyValue
	size   int
}

func DefaultTraceLogger() *TraceLogger {
	if defaultTraceLogger != nil {
		return WithLogger(defaultTraceLogger)
	}
	defaultTraceLogger = &TraceLogger{
		logger: InstanceZap.WithOptions(zap.AddCallerSkip(1)),
	}
	return WithLogger(defaultTraceLogger)
}
func NewMainLogger() *TraceLogger {
	logger := &TraceLogger{
		logger: DefaultTraceLogger().logger,
	}
	logger.Append(TRACE_ID, "main")
	return logger
}
func NewLogger(logger *zap.SugaredLogger) *TraceLogger {
	return &TraceLogger{
		logger: logger,
	}
}
func WithLogger(logger *TraceLogger) *TraceLogger {
	stack := make([]TraceKeyValue, len(logger.stack))
	for i := range logger.stack {
		stack[i] = logger.stack[i]
	}

	return &TraceLogger{
		logger: logger.logger,
		size:   logger.size,
		stack:  stack,
	}
}
func Info(args ...any) {
	DefaultTraceLogger().Info(args...)
}

func Debug(args ...any) {
	DefaultTraceLogger().Debug(args...)
}

func Warn(args ...any) {
	DefaultTraceLogger().Warn(args...)
}

func Error(args ...any) {
	DefaultTraceLogger().Error(args...)
}
func (ew *TraceLogger) Exists(key string) bool {
	for i := range ew.stack {
		if ew.stack[i].Key == key {
			return true
		}
	}
	return false
}
func (ew *TraceLogger) Append(key, value string) {
	if ew.Exists(key) {
		return
	}
	ew.stack = append(ew.stack, TraceKeyValue{
		Key:      key,
		Value:    value,
		FmtValue: "[" + value + "]\t",
	})
	ew.size += 1
}
func (ew *TraceLogger) Get(key string) string {
	for i := range ew.stack {
		if ew.stack[i].Key == key {
			return ew.stack[i].Value
		}
	}
	return ""
}
func (ew *TraceLogger) Stack() []TraceKeyValue {
	if len(ew.stack) == 0 {
		return nil
	}
	result := make([]TraceKeyValue, len(ew.stack))
	copy(result, ew.stack)
	return result
}
func (ew *TraceLogger) Remove(key string) {
	var stack []TraceKeyValue
	for i := range ew.stack {
		if ew.stack[i].Key != key {
			stack = append(stack, ew.stack[i])
		}
	}
	ew.stack = stack
	ew.size = len(ew.stack)
}
func (ew *TraceLogger) AppendFmt(key, value string, fmtValue string) {
	if ew.Exists(key) {
		return
	}
	if !strings.HasSuffix(fmtValue, "\t") {
		fmtValue = fmtValue + "\t"
	}
	ew.stack = append(ew.stack, TraceKeyValue{
		Key:      key,
		Value:    value,
		FmtValue: fmtValue,
	})
	ew.size = len(ew.stack)
}

func (ew *TraceLogger) Info(args ...any) {
	if ew.logger.Level() <= zap.InfoLevel {
		ew.logger.Info(ew.Base(args)...)
	}

}

func (ew *TraceLogger) Debug(args ...any) {
	if ew.logger.Level() <= zap.DebugLevel {
		ew.logger.Debug(ew.Base(args)...)
	}

}

func (ew *TraceLogger) Warn(args ...any) {
	if ew.logger.Level() <= zap.WarnLevel {
		ew.logger.Warn(ew.Base(args)...)
	}
}

func (ew *TraceLogger) Error(args ...any) {
	if ew.logger.Level() <= zap.ErrorLevel {
		ew.logger.Error(ew.Base(args)...)
	}

}

func (ew *TraceLogger) Fatal(args ...any) {
	if ew.logger.Level() <= zap.FatalLevel {
		ew.logger.Fatal(ew.Base(args)...)
	}

}

func (ew *TraceLogger) Panic(args ...any) {
	if ew.logger.Level() <= zap.PanicLevel {
		ew.logger.Panic(ew.Base(args)...)
	}

}

func (ew *TraceLogger) Infof(template string, args ...any) {
	if ew.logger.Level() <= zap.InfoLevel {
		ew.logger.Info(ew.Base(GetMessage(template, args))...)
	}
}

func (ew *TraceLogger) Debugf(template string, args ...any) {
	if ew.logger.Level() <= zap.DebugLevel {
		ew.logger.Debug(ew.Base(GetMessage(template, args))...)
	}
}

func (ew *TraceLogger) Warnf(template string, args ...any) {
	if ew.logger.Level() <= zap.WarnLevel {
		ew.logger.Warn(ew.Base(GetMessage(template, args))...)
	}
}

func (ew *TraceLogger) Errorf(template string, args ...any) {
	if ew.logger.Level() <= zap.ErrorLevel {
		ew.logger.Error(ew.Base(GetMessage(template, args))...)
	}
}

func (ew *TraceLogger) Fatalf(template string, args ...any) {
	if ew.logger.Level() <= zap.FatalLevel {
		ew.logger.Fatal(ew.Base(GetMessage(template, args))...)
	}
}

func (ew *TraceLogger) Panicf(template string, args ...any) {
	if ew.logger.Level() <= zap.PanicLevel {
		ew.logger.Panic(ew.Base(GetMessage(template, args))...)
	}
}

func (ew *TraceLogger) Log(lvl zapcore.Level, template string, args ...interface{}) {
	if ew.logger.Level() <= lvl {
		ew.logger.Log(lvl, ew.Base(GetMessage(template, args))...)
	}
}

func (ew *TraceLogger) Logf(lvl zapcore.Level, template string, args ...interface{}) {
	if ew.logger.Level() <= lvl {
		ew.logger.Log(lvl, ew.Base(GetMessage(template, args))...)
	}
}

func (ew *TraceLogger) Base(args ...any) []any {
	if ew.size == 0 {
		return args
	}
	result := make([]any, len(args)+ew.size)
	i := 0
	for ele := range ew.stack {
		result[i] = ew.stack[ele].FmtValue
		i++
	}

	for ele := range args {
		result[i] = args[ele]
		i++
	}

	return result
}
func GetMessage(template string, fmtArgs []interface{}) string {
	if len(fmtArgs) == 0 {
		return template
	}

	if template != "" {
		return fmt.Sprintf(template, fmtArgs...)
	}

	if len(fmtArgs) == 1 {
		if str, ok := fmtArgs[0].(string); ok {
			return str
		}
	}
	return fmt.Sprint(fmtArgs...)
}
